﻿// Less2DB.NET framework
// http://code.google.com/p/less2db/
//
// Copyright © Wangzhongwu, 2011
// alex.wzw@gmail.com
//

namespace Less2DB
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data;
    using System.Reflection;
    using System.Collections.ObjectModel;
    using System.Linq.Expressions;

    /// <summary>
    /// 实体对象的抽象类。
    /// </summary>
    public abstract class EntityBase : Less2DB.Interfaces.IEntity, ICloneable
    {
        class EntityCache : Dictionary<string, EntityConfig> { }

        struct EntityConfig
        {
            public EntityConfig(ref string pkFieldName, ref PropertyInfo[] fields, ref PropertyInfo[] writableFields)
            {
                this.PrimaryKeyFieldName = pkFieldName;
                this.Fields = fields;
                this.WritableFields = writableFields;
            }

            public readonly string PrimaryKeyFieldName;
            public readonly PropertyInfo[] Fields;
            public readonly PropertyInfo[] WritableFields;
        }

        /// <summary>
        /// 存储实体配置的静态对象。
        /// </summary>
        private static EntityCache _entityCache;

        static EntityBase()
        {
            _entityCache = new EntityCache();
        }

        public EntityBase(Less2DB.Interfaces.IDAL dal, string connectionString)
        {
            _dal = dal;
            _connectionString = connectionString;

            if (!_entityCache.ContainsKey(IEntity.TableName))
            {
                // TODO: 实体配置未被缓存，进行首次初始化；

                // 反射读取配置信息；
                this.Reflection();

                // 添加配置信息到缓存；
                lock (_entityCache)
                {
                    if (!_entityCache.ContainsKey(IEntity.TableName))
                    {
                        _entityCache.Add(IEntity.TableName, new EntityConfig(ref _PrimaryKey, ref _FieldNames, ref _FieldNamesForCU));
                    }
                }
            }
            else
            {
                // TODO: 从缓存初始化实体配置；

                this.InitializeFromCache(IEntity.TableName);
            }
        }

        /// <summary>
        /// 反射读取实体配置。
        /// </summary>
        private void Reflection()
        {
            #region GetDBField 数据库字段描述数据的搜索程序

            var GetDBField = new Func<PropertyInfo, DatabaseFieldAttribute>(delegate(PropertyInfo prop)
            {
                var attrs = prop.GetCustomAttributes(typeof(DatabaseFieldAttribute), false);

                if (attrs.Length > 0)
                {
                    return attrs[0] as DatabaseFieldAttribute;
                }
                else
                {
                    return null;
                }
            });

            #endregion

            var properties = from prop in this.GetType().GetProperties() select new { DBField = GetDBField(prop), Property = prop };
            var dbProperties = from prop in properties where prop.DBField != null select prop;

            _FieldNames = (from prop in dbProperties select prop.Property).ToArray();
            _FieldNamesForCU = (from prop in dbProperties where !prop.DBField.IsIncrementField select prop.Property).ToArray();
            _PrimaryKey = (from prop in dbProperties where prop.DBField.IsPrimaryKey select prop.Property.Name).FirstOrDefault();
        }

        /// <summary>
        /// 从缓存初始化实体。
        /// </summary>
        /// <param name="config">配置对象</param>
        private void InitializeFromCache(string tableName)
        {
            var config = _entityCache[tableName];

            _PrimaryKey = config.PrimaryKeyFieldName;
            _FieldNames = config.Fields;
            _FieldNamesForCU = config.WritableFields;
        }

        #region Less2DB.Interface.IEntity 成员

        private Less2DB.Interfaces.IDAL _dal;
        Less2DB.Interfaces.IDAL Less2DB.Interfaces.IEntity.DAL
        {
            get { return _dal; }
        }

        private string _connectionString;
        string Less2DB.Interfaces.IEntity.ConnectionString
        {
            get { return _connectionString; }
        }

        string Less2DB.Interfaces.IEntity.TableName { get { return this.TableName; } }

        protected abstract string TableName { get; }

        private string _PrimaryKey;
        string Less2DB.Interfaces.IEntity.PrimaryKey
        {
            get { return _PrimaryKey; }
        }

        private PropertyInfo[] _FieldNames;
        string[] Less2DB.Interfaces.IEntity.FieldNames
        {
            get { return (from f in _FieldNames select f.Name).ToArray(); }
        }

        private PropertyInfo[] _FieldNamesForCU;
        string[] Less2DB.Interfaces.IEntity.FieldNamesForCU
        {
            get { return (from f in _FieldNamesForCU select f.Name).ToArray(); }
        }

        IDbDataParameter[] Interfaces.IEntity.GetParameters(string[] fieldNames)
        {
            var list = new List<IDbDataParameter>();

            #region 循环并搜索与字段列表对应的属性，并填充查询参数列表
            foreach (string name in fieldNames)
            {
                // 在所有 CU 属性列表中搜索与循环体 name 相匹配的属性对象；
                var linq = (from prop in _FieldNames where prop.Name == name select prop).FirstOrDefault();

                if (linq != null)
                {
                    var propValue = linq.GetValue(this, null);
                    var param = this.IEntity.DAL.CreateParameter("@" + name, propValue);

                    list.Add(param);
                }
            } 
            #endregion

            return list.ToArray();
        }

        #endregion

        #region ICloneable 成员

        public abstract object Clone();

        #endregion

        /// <summary>
        /// 返回当前实体实现的 Less2DB.Interface.IEntity 接口。
        /// </summary>
        private Less2DB.Interfaces.IEntity IEntity
        {
            get { return this as Less2DB.Interfaces.IEntity; }
        }
    }
}